/*******************************************************************
 *
 * Copyright (C), 2021-2022, LS, All rights reserved.
 *
 * Author      : 
 * Create Time : 2021-09-17
 * Version     : v0.1
 * Description : 
 *******************************************************************/

 /*
 * INCLUDE FILES
 ****************************************************************************************
 */
#include <string.h>
#include "bcomdef.h"
#include "OSAL.h"
#include "linkdb.h"
#include "att.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "peripheral.h"
#include "gattservapp.h"
#include "ls_ble_type.h"
#include "ls_ble_file.h"
#include "ls_ble_sdk_config.h"

#if (LS_BLE_FILE_SVC_ENABLE)

/*
 * DEFINES
 ****************************************************************************************
 */
#define BLE_FILE_LOG_EN     1

#if BLE_FILE_LOG_EN
#include "ls_ble_log.h"
#define BLE_FILE_LOG        LS_BLE_LOG_INFO
#else
#define BLE_FILE_LOG(...)
#endif

/*********************************************************************
 * MACROS
 */
 
#define FILE_CMD_NOTIFY_HANDLE	4

/*********************************************************************
 * CONSTANTS
 */


/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
// FILE GATT Profile Service UUID
CONST uint8 fileServUUID[ATT_BT_UUID_SIZE] =
{
    LO_UINT16(FILE_SERVICE_UUID), HI_UINT16(FILE_SERVICE_UUID)
};

// FILE Write Characteristic UUID: 0xFFF1
CONST uint8 fileProfilecharWriteUUID[ATT_BT_UUID_SIZE] =
{ 
    LO_UINT16(FILE_SERVICE_WRITE_UUID), HI_UINT16(FILE_SERVICE_WRITE_UUID)
};

// FILE Notify Characteristic UUID: 0xFFF2
CONST uint8 fileProfilecharNotifyUUID[ATT_BT_UUID_SIZE] =
{ 
    LO_UINT16(FILE_SERVICE_NOTIFY_UUID), HI_UINT16(FILE_SERVICE_NOTIFY_UUID)
};

// FILE DATA Characteristic notification UUID: 0xFFF3
CONST uint8 fileProfilecharDataUUID[ATT_BT_UUID_SIZE] =
{ 
    LO_UINT16(FILE_SERVICE_DATA_UUID), HI_UINT16(FILE_SERVICE_DATA_UUID)
};

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

/*********************************************************************
 * Profile Attributes - variables
 */

// FILE Profile Service attribute
static CONST gattAttrType_t fileProfileService = { ATT_BT_UUID_SIZE, fileServUUID };


// FILE Profile Write Properties
static uint8_t fileProfileWriteProps = GATT_PROP_WRITE;

// FILE Profile Write Value
static uint8_t fileProfileWriteValue[FILE_MAX_DATA_LEN];

// FILE Profile Notify Characteristic Properties
static uint8 fileProfileCharNotifyProps = GATT_PROP_NOTIFY;

// FILE Profile Notify Value
static uint8 fileProfileCharNtfValue[20];

static gattCharCfg_t fileProfileCharNtfCCC[GATT_MAX_NUM_CONN];


// FILE Profile Data Properties
static uint8_t fileProfileDataProps = GATT_PROP_WRITE_NO_RSP;

// FILE Profile Data Packet Value
static uint8_t fileProfileDataValue[FILE_MAX_DATA_LEN];


static file_profile_cb_t *file_profile_cb = NULL;

/*********************************************************************
 * Profile Attributes - Table
 */

static gattAttribute_t fileProfileAttrTbl[] = 
{
    // FILE Profile Service
    { 
        { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
        GATT_PERMIT_READ,                         /* permissions */
        0,                                        /* handle */
        (uint8 *)&fileProfileService            /* pValue */
    },
    
    
    // FILE Write Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &fileProfileWriteProps 
    },

      // FILE Write Value
      { 
        { ATT_BT_UUID_SIZE, fileProfilecharWriteUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8*)&fileProfileWriteValue 
      },

    // FILE Notify Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &fileProfileCharNotifyProps 
    },

      // FILE Notify Value
      { 
        { ATT_BT_UUID_SIZE, fileProfilecharNotifyUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8*)&fileProfileCharNtfValue 
      },
      
      // FILE Notify configuration
      { 
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8_t *)&fileProfileCharNtfCCC
      },  

    // FILE Data Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &fileProfileDataProps 
    },

      // FILE Data Value
      { 
        { ATT_BT_UUID_SIZE, fileProfilecharDataUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8*)&fileProfileDataValue 
      },
};

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static uint8 fileProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 *pLen, uint16 offset, uint8 maxLen );
static bStatus_t fileProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 len, uint16 offset );

/*********************************************************************
 * PROFILE CALLBACKS
 */
// FILE Profile Service Callbacks
CONST gattServiceCBs_t fileProfileCBs =
{
    fileProfile_ReadAttrCB,  // Read callback function pointer
    fileProfile_WriteAttrCB, // Write callback function pointer
    NULL                       // Authorization callback function pointer
};

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      fileProfile_AddService
 *
 * @brief   Initializes the Rope Profile service by registering
 *          GATT attributes with the GATT server.
 *
 * @param   services - services to add. This is a bit map and can
 *                     contain more than one service.
 *
 * @return  Success or Failure
 */
int file_service_init(file_profile_cb_t *cb)
{
    uint8 status = SUCCESS;

    // Initialize Client Characteristic Configuration attributes
    GATTServApp_InitCharCfg( INVALID_CONNHANDLE, fileProfileCharNtfCCC );

    // Register GATT attribute list and CBs with GATT Server App
    status = GATTServApp_RegisterService( fileProfileAttrTbl, 
                                          GATT_NUM_ATTRS( fileProfileAttrTbl ),
                                          &fileProfileCBs );
    if(cb)
    {
        file_profile_cb = cb;
        return ( SUCCESS );
    }

    return ( status );
}


/*********************************************************************
 * @fn          fileProfile_ReadAttrCB
 *
 * @brief       Read an attribute.
 *
 * @param       connHandle - connection message was received on
 * @param       pAttr - pointer to attribute
 * @param       pValue - pointer to data to be read
 * @param       pLen - length of data to be read
 * @param       offset - offset of the first octet to be read
 * @param       maxLen - maximum length of data to be read
 *
 * @return      Success or Failure
 */
static uint8 fileProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 *pLen, uint16 offset, uint8 maxLen )
{
    bStatus_t status = SUCCESS;

    // If attribute permissions require authorization to read, return error
    if ( gattPermitAuthorRead( pAttr->permissions ) )
    {
        // Insufficient authorization
        return ( ATT_ERR_INSUFFICIENT_AUTHOR );
    }

    // Make sure it's not a blob operation (no attributes in the profile are long)
    if ( offset > 0 )
    {
        return ( ATT_ERR_ATTR_NOT_LONG );
    }

    if ( pAttr->type.len == ATT_BT_UUID_SIZE )
    {
        // 16-bit UUID
        uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
        switch ( uuid )
        {
            default:
                // Should never get here! (characteristics 3 and 4 do not have read permissions)
                *pLen = 0;
                status = ATT_ERR_ATTR_NOT_FOUND;
            break;
        }
    }
    else
    {
        // 128-bit UUID
        *pLen = 0;
        status = ATT_ERR_INVALID_HANDLE;
    }

    return ( status );
}

/*********************************************************************
 * @fn      fileProfile_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 *
 * @return  Success or Failure
 */
static bStatus_t fileProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 len, uint16 offset )
{
    bStatus_t status = SUCCESS;
    file_evt_t evt = {
        .evt_type = FILE_EVT_INVALID,
        .length = 0
    };
    
    // If attribute permissions require authorization to write, return error
    if( gattPermitAuthorWrite( pAttr->permissions ) )
    {
        // Insufficient authorization
        return ( ATT_ERR_INSUFFICIENT_AUTHOR );
    }

    if( pAttr->type.len == ATT_BT_UUID_SIZE )
    {
        // 16-bit UUID
        uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
        switch ( uuid )
        {
            case FILE_SERVICE_WRITE_UUID: {
                evt.evt_type = FILE_EVT_RECEIVE_CMD;
                evt.length = len;
                evt.p_data = pValue;
            } break;
            
            case FILE_SERVICE_DATA_UUID: {
                evt.evt_type = FILE_EVT_RECEIVE_FILE_DATA;
                evt.length = len;
                evt.p_data = pValue;
            } break;

            case GATT_CLIENT_CHAR_CFG_UUID: {
                status = GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_NOTIFY);
                if(status == SUCCESS)
                {
                    uint16 charCfg = BUILD_UINT16(pValue[0], pValue[1]);
                    evt.evt_type = (charCfg == GATT_CFG_NO_OPERATION) ? FILE_EVT_NOTIFICATION_DISABLED : FILE_EVT_NOTIFICATION_ENABLED;
                }
            } break;

            default:
                // Should never get here! (characteristics 2 and 4 do not have write permissions)
                status = ATT_ERR_ATTR_NOT_FOUND;
            break;
        }
    }
    else
    {
        // 128-bit UUID
        status = ATT_ERR_INVALID_HANDLE;
    }

    if(file_profile_cb && FILE_EVT_INVALID != evt.evt_type) {
        file_profile_cb->evt_handler(&evt);
    }

    return ( status );
}

/*********************************************************************
 * @fn          FileProfile_HandleConnStatusCB
 *
 * @brief       Rope Profile link status change handler function.
 *
 * @param       connHandle - connection handle
 * @param       changeType - type of change
 *
 * @return      none
 */
void fileProfile_HandleConnStatusCB( uint16 connHandle, uint8 changeType )
{
    // Make sure this is not loopback connection
    if ( connHandle != LOOPBACK_CONNHANDLE )
    {
        // Reset Client Char Config if connection has dropped
        if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
             ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && 
               ( !linkDB_Up( connHandle ) ) ) )
        { 
            GATTServApp_InitCharCfg( connHandle, fileProfileCharNtfCCC );
        }
    }
}


int file_notify_data(uint8_t *p_data, uint16_t length)
{
    uint16 value;
    uint16 connHandle;
    attHandleValueNoti_t ntf;

    if(length > ATT_MTU_SIZE) {
        return LS_BLE_ERR_INVALID_LENGTH;
    }

    GAPRole_GetParameter(GAPROLE_CONNHANDLE, &connHandle);
    value = GATTServApp_ReadCharCfg( connHandle, fileProfileCharNtfCCC);

    // If notification enabled
    if( value & GATT_CLIENT_CFG_NOTIFY )
    {
        // Set the handle
        ntf.handle = fileProfileAttrTbl[FILE_CMD_NOTIFY_HANDLE].handle;
        memcpy(ntf.value, p_data, length);
        ntf.len = length;

        // Send the notify
        bStatus_t st = GATT_Notification(connHandle, &ntf, FALSE);
        if(st != SUCCESS)
        {
            BLE_FILE_LOG("st: %x\n", st);
        }
        else {
            return LS_BLE_SUCCESS;
        }
    }

    return LS_BLE_ERR_NTF_FAIL;
}


#endif      //#if (LS_BLE_FILE_SVC_ENABLE)
